package com.atguigu.srb.core.service.impl;

import com.atguigu.common.exception.Assert;
import com.atguigu.common.exception.BusinessException;
import com.atguigu.common.results.ResultCodeEnum;
import com.atguigu.common.utils.MD5;
import com.atguigu.srb.base.utils.JwtUtils;
import com.atguigu.srb.core.pojo.entity.UserAccount;
import com.atguigu.srb.core.pojo.entity.UserInfo;
import com.atguigu.srb.core.mapper.UserInfoMapper;
import com.atguigu.srb.core.pojo.entity.UserLoginRecord;
import com.atguigu.srb.core.pojo.query.UserQuery;
import com.atguigu.srb.core.pojo.vo.LoginVo;
import com.atguigu.srb.core.pojo.vo.UserIndexVo;
import com.atguigu.srb.core.pojo.vo.UserInfoVo;
import com.atguigu.srb.core.service.UserAccountService;
import com.atguigu.srb.core.service.UserInfoService;
import com.atguigu.srb.core.service.UserLoginRecordService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.List;
import java.util.UUID;

/**
 * <p>
 * 用户基本信息 服务实现类
 * </p>
 *
 * @author Atguigu
 * @since 2022-10-24
 */
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    UserLoginRecordService userLoginRecordService;

    public UserInfo checkAccount(String token) {
        //1、判断用户是否登录
        Long userId = JwtUtils.getUserId(token);
        //2、判断用户账户是否被锁定+用户是否绑定过汇付宝账户
        UserInfo userInfo = this.getById(userId);
        if(userInfo.getStatus()!=1){
            throw new BusinessException(ResultCodeEnum.LOGIN_LOKED_ERROR);
        }
        if(userInfo.getBindStatus()!=1){
            throw new BusinessException(ResultCodeEnum.USER_NO_BIND_ERROR);
        }
        return userInfo;
    }
    @Override
    public void register(UserInfoVo userInfoVo) {
        //1、数据校验
        Integer userType = userInfoVo.getUserType();
        String code = userInfoVo.getCode();
        String mobile = userInfoVo.getMobile();
        String password = userInfoVo.getPassword();
        //所有的参数不能为空
        Assert.assertObjNotNull(userType);
        Assert.assertStrNotNull(code);
        Assert.assertStrNotNull(mobile);
        Assert.assertStrNotNull(password);
        //2、手机号码重复注册校验
        int count = this.count(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getMobile, userInfoVo.getMobile()));
        Assert.assertCountEq0(count);

        //3、校验验证码是否正确： 手机号码是否正确  验证码是否正确
        //使用注册手机号码拼接key 获取redis中缓存的验证码  和code进行比较
        String key = "sms:code:"+mobile;
        String redisCode = redisTemplate.opsForValue().get(key);
        //redis缓存的验证码为空  抛出异常      //比较验证码是否一致
        Assert.assertCodeNotEq(redisCode,code);

        //验证码正确 保存注册数据到数据库
        UserInfo userInfo = new UserInfo();
        BeanUtils.copyProperties(userInfoVo , userInfo);
        userInfo.setNickName("谷粉"+ UUID.randomUUID().toString().substring(0,6));
        userInfo.setHeadImg("https://sh220623-srb.oss-cn-shanghai.aliyuncs.com/avatar/2022/10-29/1667025357877_b3e6f6.jpg");
        //4、密码加密：  加盐 每个用户生成一个随机的字符串和密码组合再加密防止暴力破解
        String salt = "abc123!.$#";//UUID.randomUUID().toString().substring(0,6);
//        userInfo.setSalt(salt)
        String encodePwd = MD5.encrypt( salt+ MD5.encrypt(userInfo.getPassword()));
        userInfo.setPassword(encodePwd);
        //5、保存到数据库
        this.save(userInfo);
        //删除redis缓存的验证码
        redisTemplate.delete(key);
    }
    @Transactional(rollbackFor = {Exception.class}) //
    @Override
    public String login(LoginVo loginVo,String host) {
        String mobile = loginVo.getMobile();
        String password = loginVo.getPassword();
        Integer userType = loginVo.getUserType();
        Assert.assertStrNotNull(mobile);
        Assert.assertStrNotNull(password);
        Assert.assertObjNotNull(userType);
        //1、验证账号是否正确
        UserInfo userInfo = this.getOne(new LambdaQueryWrapper<UserInfo>()
                .eq(UserInfo::getMobile, mobile)
                .eq(UserInfo::getUserType , userType));
        if(userInfo==null){//账号不存在
            throw new BusinessException(ResultCodeEnum.LOGIN_MOBILE_ERROR);
        }
        //2、账号存在  判断密码是否正确
        //2.1 对登录的明文密码进行加密
        String salt = "abc123!.$#";//UUID.randomUUID().toString().substring(0,6);
//        userInfo.setSalt(salt)
        String encodePwd = MD5.encrypt( salt+ MD5.encrypt(password));
        //2.2 判断登录密码加密后 和数据库密码是否一致
        if(!encodePwd.equals(userInfo.getPassword())){
            //登录密码错误
            throw new BusinessException(ResultCodeEnum.LOGIN_PASSWORD_ERROR);
        }
        //3、判断账号是否被禁用
        if(userInfo.getStatus()==0){
            throw new BusinessException(ResultCodeEnum.LOGIN_LOKED_ERROR);
        }

        //4、登录成功：将用户信息转为jwt 字符串返回
        String token = JwtUtils.createToken(userInfo.getId(), userInfo.getNickName());
        //保存登录日志
        UserLoginRecord userLoginRecord = new UserLoginRecord();
        userLoginRecord.setIp(host);
        userLoginRecord.setUserId(userInfo.getId());
        userLoginRecordService.save(userLoginRecord);
        return token;
    }

    @Override
    public void getUserPage(Page<UserInfo> page, UserQuery userQuery) {
        String mobile = userQuery.getMobile();
        Integer status = userQuery.getStatus();
        Integer userType = userQuery.getUserType();
        LambdaQueryWrapper<UserInfo> queryWrapper = Wrappers.lambdaQuery();
        if(!StringUtils.isEmpty(mobile)){
            queryWrapper.likeRight(UserInfo::getMobile , mobile);
        }
        if(status!=null){
            queryWrapper.eq(UserInfo::getStatus , status);
        }
        if(userType!=null){
            queryWrapper.eq(UserInfo::getUserType , userType);
        }
        //参数1：page传递分页的条件   参数2：warpper传递查询数据的过滤条件
        this.page(page , queryWrapper);
    }
    @Autowired
    UserAccountService userAccountService;
    @Override
    public UserIndexVo getUserIndexVo(String token) {
        //1、获取用户id
        Long userId = JwtUtils.getUserId(token);
        //2、查询用户信息
        UserInfo userInfo = this.getById(userId);
        //3、查询最近登录时间
        UserLoginRecord userLoginRecord = userLoginRecordService.getOne(new LambdaQueryWrapper<UserLoginRecord>()
                .eq(UserLoginRecord::getUserId, userId)
                .orderByDesc(UserLoginRecord::getCreateTime)
                .last("limit 1"));//使用创建时间倒序排序 获取第一条数据
        //4、查询用户充值成功的账户信息
        List<UserAccount> userAccounts = userAccountService.list(new LambdaQueryWrapper<UserAccount>()
                .eq(UserAccount::getUserId, userId)
                .eq(UserAccount::getStatus,1));
        BigDecimal amount = new BigDecimal("0");
        BigDecimal freezeAmount = new BigDecimal("0");
        if(!CollectionUtils.isEmpty(userAccounts)){
            //累加账户金额
            amount = userAccounts.stream()
                    // 遍历集合 将每个对象转为其他类型的对象返回得到一个新的集合
                    .map(userAccount -> userAccount.getAmount())
                    // 对前面的集合 进行遍历，每两个元素 进行处理
                    .reduce((a,b)->a.add(b)).get(); //get获取reduce计算的结果
            //一个sql需要查询所有用户的数据同时查询到分组结果 ，mysql8提供了窗口函数可以解决该问题
            freezeAmount = userAccounts.stream()
                    // 遍历集合 将每个对象转为其他类型的对象返回得到一个新的集合
                    .map(userAccount -> userAccount.getFreezeAmount())
                    // 对前面的集合 进行遍历，每两个元素 进行处理
                    .reduce((a,b)->a.add(b)).get();
        }
        UserIndexVo userIndexVo = new UserIndexVo();
        userIndexVo.setUserType(userInfo.getUserType());
        userIndexVo.setNickName(userInfo.getNickName());
        userIndexVo.setBindStatus(userInfo.getBindStatus());
        userIndexVo.setHeadImg(userInfo.getHeadImg());
        if(userLoginRecord!=null){
            userIndexVo.setLastLoginTime(userLoginRecord.getCreateTime());
        }
        userIndexVo.setAmount(amount);
        userIndexVo.setFreezeAmount(freezeAmount);
        return userIndexVo;
    }
}
